﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Reflection;
using Mss.Common.Helpers;

namespace Mss.Data.Repositories
{
    internal abstract class Repository<T> :NotifyPropertyChangedBase, IRepository<T> where T : EntityObject
    {
        #region Fields

        private ReadOnlyObservableCollection<T> _cachedObjects;
        private ObjectQuery _objectSet;

        #endregion

        #region Constructor

        protected Repository()
        {
            if (DomainContext == null)
            {
                DomainContext = new ModelDomainContext();
                DomainContext.ObjectStateManager.ObjectStateManagerChanged += onObjectStateManagerChanged;
            }
        }

        #endregion

        #region Properties

        internal static ModelDomainContext DomainContext { get; private set; }

        public ReadOnlyObservableCollection<T> CachedObjects
        {
            get { return _cachedObjects; }
            private set
            {
                if (_cachedObjects == value) return;
                _cachedObjects = value;
                RaisePropertyChanged("CachedObjects");
            }
        }

        public bool HasChanges
        {
            get
            {
                bool isChanged = false;
                isChanged |= DomainContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Any();
                isChanged |= DomainContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Any();
                isChanged |= DomainContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Any();
                return isChanged;
            }
        }

        #endregion

        #region Methods

        public virtual void RefreshCache()
        {
            CachedObjects = new ReadOnlyObservableCollection<T>(new ObservableCollection<T>((IEnumerable<T>)_objectSet));
        }

        public virtual void SaveChanges()
        {
            DomainContext.SaveChanges();
            RefreshCache();
        }

        public void RevertObjectChanges(T obj)
        {
            DomainContext.Refresh(RefreshMode.StoreWins, obj);
        }

        public virtual void AddObject(T obj)
        {
            //Add object to the object set.
            MethodInfo method = _objectSet.GetType().GetMethod("AddObject");
            method.Invoke(_objectSet, new object[] { obj });
        }

        public void DeleteObject(T obj)
        {
            //Delete object from the object set.
            MethodInfo method = _objectSet.GetType().GetMethod("DeleteObject");
            method.Invoke(_objectSet, new object[] { obj });
        }

        public virtual void Load() 
        {
            _objectSet = DomainContext.CreateObjectSet<T>();

            RefreshCache();

            if (CachedObjects != null)
            {
                DomainContext.Refresh(RefreshMode.StoreWins, CachedObjects);
            }
        }

        private void onObjectStateManagerChanged(object sender, CollectionChangeEventArgs e)
        {
            RaisePropertyChanged("HasChanges");
        }

        #endregion
    }
}